编写who命令

通过编写who命令来了解 utmp 数据结构, 和Unix文件的打开、读取和关闭

从文件读取数据结构

查找从读取文件相关

$ man -k file | grep read

最有可能的是read(2)

$ man 2 read

这个系统调用可以将文件中一定数目的字节读入缓冲区, 因为每次都要读入一个数据结构, 所以要用sizeof(struct utmp)来指定每次读入的字节数. read需要一个文件描述符作为参数, 在read的联机帮助中的最后部分有对 open(2) 引用

$ man 2 open

查看 open 的联机帮助, 从open中又可以找到对 close的引用

使用open/read和close

1. 打开一个文件: open

这个系统调用在进程和文件之间建立一条连接, 这个连接被称为文件描述符

  open
目标 打开一个文件
头文件 #include <fcntl.h>
函数原型 int fd = open(char *name, int how)
参数 name 文件名
  how O_RDONLY, O_WRONLY, or O_RDWR
返回值 -1 error int success

2.从文件读取数据:read

  read
目标 把数据读取到缓冲区
头文件 #include <unistd.h>
函数原型 size_t numread = read(int fd, void *buf, size_t qty)
参数 fd 文件描述, buf 缓冲区, qty 要读取的字节数
返回值 -1 error, numread success

3. 关闭文件: close

  close
目标 关闭一个文件
头文件 #include <unistd.h>
函数原型 int result = close(int fd)
参数 fd 文件描述符
返回值 -1 error, 0 success

who1.c

#include <stdio.h>
#include <utmp.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>

#define SHOWHOST

void show_time(long);
void show_info(struct utmp *);

int main()
{
    struct utmp current_record;
    int utmpfd, reclen = sizeof(current_record);

    if ((utmpfd = open(UTMP_FILE, O_RDONLY)) == -1) {
        perror(UTMP_FILE);
        exit(1);
    }
    while (read(utmpfd, &current_record, reclen) == reclen){
        show_info(&current_record);
    }
    close(utmpfd);
    return 0;
}

void show_info(struct utmp * utbufp)
{
    /*
     * USER_PROCESS 表示一个真实登陆的用户
     */
    if (utbufp->ut_type != USER_PROCESS)
        return;

    // 登陆用户名(教程上是utbufp->ut_name, 实际上是utbufp->ut_user)
    printf("%-8.8s", utbufp->ut_user);
    printf(" ");
    // 登陆的终端(tty1~6或者pts/n)
    printf("%-8.8s", utbufp->ut_line);
    // 登陆时间(书上是utbufp->ut_time, 实际上是utbufp->ut_tv.tv_sec)
    show_time(utbufp->ut_tv.tv_sec);
#ifdef SHOWHOST
    printf("(%s)", utbufp->ut_host);
#endif
    printf("\n");
}

void show_time(long timeval)
{
    char *cp;
    cp = ctime(&timeval);
    printf("%12.12s", cp + 4);
}